iT邦幫忙

2026 iThome 鐵人賽

DAY 26
0
Vibe Coding

一個 Vibe Coding 專案從原型到有人在用系列 第 26 篇

我沒寫過的 Reload,讓面板一片空白

  • 分享至 

  • xImage
  •  

打開你用 AI 做的 App,對著畫面按一下右鍵。跳出來的選單裡,每一項你都寫過嗎?

按了 reload,面板空了,也關不掉

6 月 22 日下午 3 點 55 分,使用者 joeleee 在 usage(我做的開源小工具,顯示 Claude Code、Codex 這兩個 AI 寫程式工具用了多少額度)開了 issue(問題回報)#42。標題是簡體中文,意思是:

點了面板裡的 reload 之後,面板什麼都不顯示了。想重開,但找不到在哪裡結束。

重現步驟只有兩步:點面板裡的 reload,然後什麼都沒了。他附的截圖裡,面板什麼都沒有,只剩深灰色的底。

usage 住在 Mac 螢幕上方的選單列。點一下圖示,會彈出一個面板,顯示各個 AI 工具的額度。面板最下面有一排按鈕,「結束」也在裡面。

所以他卡住了:面板空了,「結束」按鈕跟著不見。選單列上的圖示點下去只會開面板,沒有另外的選單。當時 usage 的程式裡,會叫 App 結束的地方,只接在面板這顆按鈕上。

那個 Reload,是系統送的

我翻了面板的網頁和各語言的介面文字,沒有一個字是 reload。最像的,是「結束」旁邊那顆「立即更新」。它只重抓額度數字,不會重新載入面板。

usage 的面板是一頁網頁,放在 WKWebView 裡。WKWebView 是 Mac 的 App 裡用來顯示網頁的元件,可以想成 App 裡內嵌一個小瀏覽器。它附帶一個右鍵選單:重新載入、上一頁、下一頁、在新視窗打開連結。這些不用寫一行程式就有。

面板上能把整頁重新載入的,只有這個右鍵選單。他點的,應該就是這裡。

這個選單從 5 月 20 日 v0.4.0 面板改成網頁那天就在了。從 v0.4.0 到 v0.22.4,版本號改了 78 次,每一版都帶著它。我在整個 git 紀錄裡搜 willOpenMenu、menuForEvent、contextmenu 這幾個處理右鍵選單常用的名字,第一次出現,就是修 #42 的那個 commit。在那之前,右鍵選單長什麼樣,一直是系統決定的。

為什麼按一下就什麼都沒了

usage 把面板放進 WKWebView 的方法,是把整頁 HTML 直接交給它,不給網址:

web_view.loadHTMLString_baseURL_(html, None)

最後那個 None,就是「不給網址」。這是 WKWebView 提供的正常用法,畫面照樣顯示得出來。問題在「重新載入」:瀏覽器的重新載入,是照網址再抓一次。

我寫了一支 40 行的小程式重現這件事:先把一小段 HTML 交給 WKWebView,叫一次 reload(),再把同一段 HTML 交給它一次。每一步都印出這頁的網址和畫面上的文字:

1. 載入面板:about:blank | 畫面文字:"usage\n\nClaude Code 42%"
2. reload() 之後:about:blank | 畫面文字:""
3. 重新塞入原本的 HTML:about:blank | 畫面文字:"usage\n\nClaude Code 42%"

第 1 步就是答案:畫面上明明有內容,網址卻是 about:blank,也就是瀏覽器的空白頁。第 2 步照這個網址重新載入,載回來的就是空白頁。第 3 步把 HTML 再交一次,內容才回來。

https://ithelp.ithome.com.tw/upload/images/20261010/20183178ap45jS0IJA.png

程式自己,也會叫 reload

修 #42 時,我請 AI 查原因。它順便找到另一個地方,也在叫 reload。

6 月 6 日,一位外部貢獻者送來 PR #29(把改好的程式送來請我合併)。CHANGELOG(每一版改了什麼的紀錄)是這樣寫的:WKWebView 負責畫網頁的那個程式,有時會自己掛掉,App 還開著,面板卻變成一片空白或灰色,要整個 App 重開才會好。PR #29 的修法是:偵測到它掛掉,就把面板重新載入。

重新載入用的,就是 reload(),跟右鍵選單的 Reload 是同一個動作。

PR #29 附了測試,CI(每次推上去自動跑的檢查)是綠的。測試裡放的是一個假的網頁元件,它的 reload() 什麼都不做,只數自己被叫了幾次:

assert view.reloads == 1

這個測試檢查的是「有沒有叫 reload」,沒檢查「叫完之後畫面還在不在」。Day 23 也遇過一樣的事:5 月 22 日補來防止 App 找不到翻譯檔的測試,只檢查翻譯檔有沒有列進打包清單,沒檢查程式去哪裡找。兩次都是測試看的那一步對了,使用者看到的那一步沒人看。

當天晚上的修法,和它怎麼驗的

6 月 22 日晚上,修正合併進去,程式改了兩處:

  • 右鍵選單打開前,加了一段程式,要把 Reload、上一頁、下一頁、打開連結、下載這幾項挑出來拿掉,只留「複製」這類文字操作。
  • 程式內部要重新載入面板時,改成把原本的 HTML 再交一次,也就是實跑的第 3 步。PR #29 那條自動恢復的路,也一起改成這樣。

當天怎麼驗的?單元測試全綠,加上 AI 回報裡的一句:「邏輯自查:使用者右鍵點系統選單 Reload 的入口已被移除。」測試裡的右鍵選單,是測試自己做的假選單。

#42 跟著自動關閉,我回覆他,v0.22.5 隨後發出。從他開 issue 到修正合併,6 個半小時。在真的面板上按一次右鍵這件事,Day 28 會回來做。

https://ithelp.ithome.com.tw/upload/images/20261010/20183178431agC8VQD.png

對著你的 App 按一次右鍵

把網頁包成 App 的做法很多,瀏覽器原本就有的功能(右鍵選單、重新整理、上一頁),常常會跟著被包進來。你的 App 只要有一塊畫面是網頁,就做一次這個檢查:

  1. 對著每一個畫面按右鍵,選單裡的每一項都點一次。畫面壞了就記下來,重開 App 再點下一項。
  2. 按 Cmd+R(重新整理的快捷鍵),再用觸控板兩指左右滑(上一頁、下一頁的手勢)。
  3. 每做完一步,看兩件事:畫面還在不在;還找不找得到結束 App 的地方。

如果你的 App 跟 usage 一樣用 loadHTMLString 載入畫面、網址給 None,可以跑我那支重現程式親眼看一次。Mac 上裝了 pyobjc(讓 Python 呼叫 Mac 原生元件的套件,pip install pyobjc)就能跑:

from AppKit import NSApplication
from Foundation import NSDate, NSMakeRect, NSRunLoop
from WebKit import WKWebView, WKWebViewConfiguration

HTML = "<html><body><h1>usage</h1><p>Claude Code 42%</p></body></html>"


def spin(seconds):
    NSRunLoop.currentRunLoop().runUntilDate_(NSDate.dateWithTimeIntervalSinceNow_(seconds))


def show(view, label):
    result = {}

    def done(value, error):
        result["v"] = value if error is None else f"錯誤 {error}"

    view.evaluateJavaScript_completionHandler_(
        "document.URL + ' | 畫面文字:' + JSON.stringify(document.body ? document.body.innerText : '')",
        done,
    )
    spin(1)
    print(f"{label}:{result.get('v')}")


NSApplication.sharedApplication()
view = WKWebView.alloc().initWithFrame_configuration_(
    NSMakeRect(0, 0, 364, 400), WKWebViewConfiguration.alloc().init()
)

view.loadHTMLString_baseURL_(HTML, None)
spin(2)
show(view, "1. 載入面板")

view.reload()
spin(2)
show(view, "2. reload() 之後")

view.loadHTMLString_baseURL_(HTML, None)
spin(2)
show(view, "3. 重新塞入原本的 HTML")

第 2 步的空白,跟 HTML 寫了什麼無關。我把 usage 修正前真正的面板 HTML 放進去再跑一次,第 2 步一樣是空的。只要網址給 None,reload 載回來的就是空白頁。

今天可以帶走的

工作單(交給 AI 的任務說明)裡那格「做完怎麼算對」,今天再加兩行(沒用過這張工作單,也可以直接把這兩行交給 AI,當作驗收要求):

做完怎麼算對:
- (Day 4、Day 8 到 Day 25 加的幾行)
- 畫面是網頁的 App:在打包好的 App 上,對每個畫面按右鍵、把選單每一項點一次,再按 Cmd+R、兩指左右滑;每一步貼出畫面截圖。程式自己會重新載入畫面的地方,要在真的網頁元件上跑一次,貼出載入後畫面上的文字;只用假物件數它被叫了幾次,不算數
- 結束、重開這類出口:列出 App 裡每一個能結束它的地方,各在哪個畫面上。全部在同一個畫面上的,寫出那個畫面壞掉時使用者怎麼結束 App

AI 寫進 App 的元件,會帶來你沒寫過的功能。你沒寫過,就很難想到去測;使用者不知道哪些是你寫的,看到就會點。

會叫 reload 的,不只右鍵選單。程式裡那個 reload(),是 PR #29 送來的;送的人,是 Day 1 開場那位 ericweichun:usage 曾把一天的 Codex 花費算成 12 倍,就是他修好的。他送的 27 個 PR,我全部合併了,每一個都附了測試。修 12 倍那次(PR #40)附的測試,守住了那個錯;PR #29 附的測試,只數了 reload 被叫幾次。同一個人送的,CI 都是綠的。我不靠讀 code 把關,怎麼分得出這兩種測試?明天看。

參考資料

  • usage 專案:https://github.com/aqua5230/usage
  • issue #42〈点击面板里的 reload 后,面板就什么都不显示了,想重新打开但找不到在哪里退出〉:https://github.com/aqua5230/usage/issues/42
  • PR #29〈fix: reload web panel after render process failures〉:https://github.com/aqua5230/usage/pull/29
  • v0.22.5 修正:https://github.com/aqua5230/usage/commit/b24bd20
  • v0.4.0 面板改用 WKWebView:https://github.com/aqua5230/usage/commit/2ad4929
  • Apple WKWebView 說明:https://developer.apple.com/documentation/webkit/wkwebview
  • Apple loadHTMLString(_:baseURL:) 說明:https://developer.apple.com/documentation/webkit/wkwebview/loadhtmlstring(_:baseurl:)

上一篇
README 推薦的那行安裝指令,從第一天就裝不起來
系列文
一個 Vibe Coding 專案從原型到有人在用 共 26 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言